package controllers

import (
	"fmt"
	"gateway-api/constants"

	"github.com/gin-gonic/gin"
	"github.com/lithammer/shortuuid/v3"
)

/*
 +----------------------------------------------------------------------
 + Title        : TccController
 + Author       : huwl
 + Version      : 2.0.0
 + Initial-Time : 2020-11-23
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : DTM tcc 分布式事务控制层: TCC主要用于处理一致性要求较高、需要较多灵活性的短事务
 + Desc         : tcc grpc控制器
 + 		        : 总结:
 +					tcc同步执行的执行一个子事务就等待返回一个结果
 +					saga则是异步执行子事务一次性提交事务,没有等待执行结果,要看结果需要到DTM的ui界面才能得知

 +				: 	1.0 tcc 失败、未完成的状态必须使用409、425
 +				: 	2.0 tcc 执行完子事务就返回结果，包括成功、失败，而saga无法得知只能看DTM的ui界面才能得知
 +				: 	3.0 tcc 多个子事务的时候,如果第一个子事务执行失败,后面的子事务将不会添加到全局事务中
 +
 + SUCCESS      : 表示成功
 + FAILURE      : 表示失败，这个失败是指确定的失败结果，不需要重试。
                  例如子事务因转账余额不足而失败，dtm服务因为事务已完成不允许再次提交等
 + ONGOING      : 表示未完成，还在正常进行中，此时dtm服务器需要采用固定间隔重试，而不是指数退避算法重试
				  对tcc不管用,在tcc模式下和FAILURE状态一样

 + other status : 表示临时错误，采用指数退避算法重试，避免出现故障或者bug，导致大量重试，导致负载过高
 + SUCCESS http : 状态码 200； grpc： err == nil
 + FAILURE http : 状态码 409 (http.StatusConflict)；grpc Code == Aborted tcc 失败、未完成的状态必须使用409、425
 + ONGOING http : 状态码 425 (http.StatusTooEarly)；grpc Code == FailedPrecondition tcc 失败、未完成的状态必须使用409、425
 +----------------------------------------------------------------------
*/
type TccController struct {
	BaseController
}

/**
 * tcc grpc 方式
 * 特别说明: 如果微服务使用的是go-micro框架，DTM将无法调用子事务原因不明，大概是go-micro对grpc进行改写
 * 如果项目使用的是go-micro框架解决方案：1.0 将微服务中需要使用DTM的服务方法添加对应的http方法，然后使用 DTM http 方式。
 *									  2.0 在微服务中添加google grpc服务详见，
 * @return Json
 * @author huwl
 */
func (control TccController) TccGrpc(ctx *gin.Context) {
	// amount, _ := strconv.Atoi(ctx.DefaultQuery("amount", "30"))
	// transInResult := ctx.DefaultQuery("trans_in_result", "")
	// transOutResult := ctx.DefaultQuery("trans_out_result", "")

	// // 获取微服务地址
	// target, err := tools.GetServiceAddress(tools.ServiceName)
	// if err != nil {
	// 	control.BaseError(ctx, control.WithMessage(err.Error()))
	// 	return
	// }

	gid := shortuuid.New()
	// err = dtmgrpc.TccGlobalTransaction(tools.DtmGrpcServer, gid, func(tcc *dtmgrpc.TccGrpc) error {
	// 	req := &pbDemo.ReqGrpc{
	// 		Amount:         int64(amount),
	// 		TransInResult:  transInResult,
	// 		TransOutResult: transOutResult,
	// 		// 表示失败，这个失败是指确定的失败结果，不需要重试。
	// 		// TransInResult: "FAILURE",
	// 		// 在tcc模式下ONGOING也会直接失败，只有saga模式ONGOING才表示未完成，还在正常进行中，此时dtm服务器需要采用固定间隔重试，而不是指数退避算法重试
	// 		// TransInResult: "ONGOING",
	// 	}
	// 	result := &emptypb.Empty{}
	// 	err := tcc.CallBranch(
	// 		req,
	// 		target+"/demo.DemoSrv/TransOut",
	// 		target+"/demo.DemoSrv/TransOutConfirm",
	// 		target+"/demo.DemoSrv/TransOutCancel",
	// 		result,
	// 	)
	// 	if err != nil {
	// 		return err
	// 	}

	// 	fmt.Println("-------------------- TransOut result --------------------", result)

	// 	err = tcc.CallBranch(
	// 		req,
	// 		target+"/demo.DemoSrv/TransIn",
	// 		target+"/demo.DemoSrv/TransInConfirm",
	// 		target+"/demo.DemoSrv/TransInCancel",
	// 		result,
	// 	)

	// 	fmt.Println("-------------------- TransIn result --------------------", result)
	// 	return err
	// })
	// // DTM状态: failed    rpc error: code = Aborted desc = 转入失败
	// // DTM状态: failed    rpc error: code = FailedPrecondition desc = 转入失败
	// if err != nil {
	// 	control.BaseError(ctx, control.WithMessage(err.Error()))
	// 	return
	// }

	control.BaseSuccess(ctx, control.WithData(gin.H{
		"gid": gid,
	}))
}

/**
 * tcc http 方式
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TccHttp(ctx *gin.Context) {
	// amount, _ := strconv.Atoi(ctx.DefaultQuery("amount", "30"))
	// transInResult := ctx.DefaultQuery("trans_in_result", "")
	// transOutResult := ctx.DefaultQuery("trans_out_result", "")
	// req := &ReqHTTP{
	// 	Amount:         amount,
	// 	TransInResult:  transInResult,
	// 	TransOutResult: transOutResult,
	// 	// 表示失败，这个失败是指确定的失败结果，不需要重试。
	// 	// TransInResult: "FAILURE",
	// 	// 在tcc模式下ONGOING也会直接失败，只有saga模式ONGOING才表示未完成，还在正常进行中，此时dtm服务器需要采用固定间隔重试，而不是指数退避算法重试
	// 	// TransInResult: "ONGOING",
	// }
	// // 执行尝试转出成功 => 执行确认转出成功 => 执行尝试转入成功 => 执行确认转入成功 => 全局事务成功

	// // 执行尝试转出成功 => 执行确认转出成功 => 执行尝试转入成功 => 执行确认转入失败 => 执行取消转入成功 => 执行取消转出成功 => 全局事务回滚完成

	// // Try 阶段：尝试执行，完成所有业务检查（一致性）, 预留必须业务资源（准隔离性）
	// // Confirm 阶段：如果所有分支的Try都成功了，则走到Confirm阶段。Confirm真正执行业务，不作任何业务检查，只使用 Try 阶段预留的业务资源
	// // Cancel 阶段：如果所有分支的Try有一个失败了，则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。
	// gid := shortuuid.New()
	// err := dtmcli.TccGlobalTransaction(tools.DtmHttpServer, gid, func(tcc *dtmcli.Tcc) (*resty.Response, error) {
	// 	resp, err := tcc.CallBranch(
	// 		req,
	// 		// 如果转出失败,则仅执行取消操作
	// 		tools.HttpServiceUrl+"/tcc/transOutTry",
	// 		// 假如Confirm/Cancel操作遇见失败会怎么样?
	// 		// 按照Tcc模式的协议,Confirm/Cancel操作是要求最终成功的
	// 		// 遇见失败的情况,都是由于临时故障或者程序bug
	// 		// dtm在Confirm/Cancel操作遇见失败时,会不断进行重试,直到成功
	// 		tools.HttpServiceUrl+"/tcc/transOutConfirm",
	// 		tools.HttpServiceUrl+"/tcc/transOutCancel",
	// 	)
	// 	if err != nil {
	// 		return resp, err
	// 	}
	// 	return tcc.CallBranch(
	// 		req,
	// 		tools.HttpServiceUrl+"/tcc/transInTry",
	// 		// 假如Confirm/Cancel操作遇见失败会怎么样?
	// 		// 按照Tcc模式的协议,Confirm/Cancel操作是要求最终成功的
	// 		// 遇见失败的情况,都是由于临时故障或者程序bug
	// 		// dtm在Confirm/Cancel操作遇见失败时,会不断进行重试,直到成功
	// 		tools.HttpServiceUrl+"/tcc/transInConfirm",
	// 		tools.HttpServiceUrl+"/tcc/transInCancel",
	// 	)
	// })
	// // 如果TransOutTcc、TransInTcc返回状态为: 409、425
	// // 当前程序会直接抛异常，{\"code\":400,\"data\":\"TransInTry\",\"message\":\"\"} FAILURE
	// // DTM中事务状态为 aborting,会一直重试
	// if err != nil {
	// 	control.BaseError(ctx, control.WithMessage(err.Error()))
	// 	return
	// }

	control.BaseSuccess(ctx, control.WithData(gin.H{
		// "gid": gid,
	}))
}

/**
 * 尝试转出
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TransOutTry(ctx *gin.Context) {
	fmt.Println("------------------- tcc TransOutTry -------------------")
	result := ctx.DefaultPostForm("trans_out_result", "")
	if result == "FAILURE" {
		// 返回409终止状态
		control.BaseError(ctx, control.WithStatusCode(constants.ResultFailure), control.WithData("TransInTry code=409 FAILURE"))
		return
	} else if result == "ONGOING" {
		// 返回表示未完成
		control.BaseError(ctx, control.WithStatusCode(constants.ResultOngoing), control.WithData("TransInTry code=425 ONGOING"))
		return
	}
	control.BaseSuccess(ctx, control.WithData("TransOutTry"))
}

/**
 * 确认转出
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TransOutConfirm(ctx *gin.Context) {
	fmt.Println("------------------- tcc TransOutConfirm -------------------")
	control.BaseSuccess(ctx, control.WithData("TransOutConfirm"))
}

/**
 * 取消转出
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TransOutCancel(ctx *gin.Context) {
	fmt.Println("------------------- tcc TransOutCancel -------------------")
	control.BaseSuccess(ctx, control.WithData("TransOutCancel"))
}

/**
 * 尝试转入
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TransInTry(ctx *gin.Context) {
	fmt.Println("------------------- tcc TransInTry -------------------")
	result := ctx.DefaultPostForm("trans_in_result", "")
	if result == "FAILURE" {
		// 返回409终止状态
		control.BaseError(ctx, control.WithStatusCode(constants.ResultFailure), control.WithData("TransInTry code=409 FAILURE"))
		return
	} else if result == "ONGOING" {
		// 返回表示未完成
		control.BaseError(ctx, control.WithStatusCode(constants.ResultOngoing), control.WithData("TransInTry code=425 ONGOING"))
		return
	}

	control.BaseSuccess(ctx, control.WithData("TransInTry SUCCESS"))
}

/**
 * 确认转入
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TransInConfirm(ctx *gin.Context) {
	fmt.Println("------------------- tcc TransInConfirm -------------------")
	control.BaseSuccess(ctx, control.WithData("TransInConfirm"))
}

/**
 * 取消转入
 *
 * @return Json
 * @author huwl
 */
func (control TccController) TransInCancel(ctx *gin.Context) {
	fmt.Println("------------------- tcc TransInCancel -------------------")
	// 返回409终止状态
	control.BaseSuccess(ctx, control.WithData("TransInCancel"))
}
